package cz.vargagab.mvc;

 import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import cz.vargagab.domain.ClientRmiNode;
import cz.vargagab.domain.LogObject;
import cz.vargagab.domain.NumberWrapper;
import cz.vargagab.domain.OperationType;
import cz.vargagab.domain.Request;
import cz.vargagab.repo.ExternalClientService;
import cz.vargagab.repo.ExternalServerService;
import cz.vargagab.repo.InternalClientService;

@Controller
public class HomePageController{
	
    @Autowired
    private ExternalServerService externalServerServiceRmi;
    
    @Autowired
    private ExternalClientService externalClientService;

	@Autowired
    ClientRmiNode thisNode;
    
    @Autowired 
    InternalClientService internalClientService;
    
    @RequestMapping(value="/",method=RequestMethod.GET)
    public String index(Model model)
    {
    	//release all requests - when user refreshes the page, the request would never be released
    	for (Request req : internalClientService.getReplys().keySet()){ 
    		for (ClientRmiNode currentNode : internalClientService.getReplys().get(req).keySet()){ //release all clients, that have gotten a request that has a failed node
        		try{
        			internalClientService.getClients().get(currentNode).sendRelease(req);
        		}catch(Exception exc){}
        	}
    		internalClientService.getQueue().remove(req); //odmazem request aj zo svojej fronty
    	}
    	internalClientService.getReplys().clear(); // necakam na ziadne requesty
    	
    	model.addAttribute("thisNode", thisNode);
    	model.addAttribute("loggedIn",internalClientService.isLoggedIn());
    	model.addAttribute("rmiNodes",internalClientService.getClientNodes());
    	model.addAttribute("rmiServer", internalClientService.getServerNode());
    	model.addAttribute("shared", internalClientService.getNumberWrap() );
    	model.addAttribute("logs", internalClientService.getLog());
        return "index";
    }

    //stisknute tlacitko write (stisk tlacitka vyvolava POST)
    //nacita sa hodnota shared - to co sa vyplni do formulara
    @RequestMapping(value="/", method=RequestMethod.POST, params="write")
    public String write(@ModelAttribute("shared") NumberWrapper shared, Model model) throws InterruptedException{
    	return	sendRequest(shared, OperationType.WRITE);
    }
    
    //stisknute tlacitko read (stisk tlacitka vyvolava POST)
    @RequestMapping(value="/", method=RequestMethod.POST, params="read")
    public String read(Model model) throws InterruptedException{
     return	sendRequest(null, OperationType.READ); //null = nechcem nic zapisovat ale len citat
    }
    
    public String sendRequest ( NumberWrapper shared, OperationType opType) throws InterruptedException{
    	
    	//otestuj pripojenie na server
    	try{
    		externalServerServiceRmi.testConnection(); //otestuj, ci server je zivy, ak nie vrat error.jsp
    	}catch(Exception ex){
    		internalClientService.getLog().add(new LogObject("Unable to access server"));
    		internalClientService.setLoggedIn(false);
    		internalClientService.setServerNode(null);
    		return "error";
    	}
    	
    	if(opType.equals(OperationType.WRITE)){
    		internalClientService.getLog().add(new LogObject("[" + thisNode.getName() +"] Trying to write " + shared.getSharedProperty()));
    	}else{
    		internalClientService.getLog().add(new LogObject("[" + thisNode.getName() +"] Trying to read shared property value"));
    	}
    	
    	//novy request
    	Request request = new Request (System.currentTimeMillis(),thisNode, opType);
    	//vlozim novy request k sebe do prioritnej fronty - automaticky sa zoradi podla timestampu (lebo Request ma metodu compareTo)
    	internalClientService.getQueue().add(request);
    	
    	//zatial prazdna mapa odpovedi - ku kazdemu klientskemu nodu je priradena hodnota true / false (ako odpoved ci uz prisla)
    	Map<ClientRmiNode,Boolean> emptyMapOfReplys = new HashMap<ClientRmiNode,Boolean>();
    	for (ClientRmiNode clientNode: internalClientService.getClientNodes()){
    		emptyMapOfReplys.put(clientNode, Boolean.FALSE);
    	}
    	internalClientService.getReplys().put(request, emptyMapOfReplys);
    	
    	for (ClientRmiNode otherNode : internalClientService.getClients().keySet()){
        	//blok catch sa vyvola len pri pade nodu, pri korektnom undeployi sa nevyvola, lebo node sa korektne odhlasi
    		try{
        		internalClientService.getClients().get(otherNode).testConnection();
        		internalClientService.getClients().get(otherNode).sendRequest(request);
        	}catch(Exception ex){
        		externalServerServiceRmi.logout(otherNode);
        		internalClientService.getLog().add(new LogObject("Sending logout for lousy " + otherNode.getName()));
        		for (ClientRmiNode currentNode : internalClientService.getClients().keySet()){ //release all clients, that have gotten a request that has a failed node
            		try{
            			internalClientService.getClients().get(currentNode).sendRelease(request);
            		}catch(Exception exc){}
            	}
        		internalClientService.getQueue().remove(request);
        		return "redirect:/";
        	}
    	}
    	
    	//mapa odpovedi ku kazdemu klientskemu nodu pre dany request
    	Map<ClientRmiNode,Boolean> mapOfReplysToCurrentRequest = internalClientService.getReplys().get(request);

    	//if cast by nemala nastavat - nedostanem odpoved, pokym nebude true
    	if (mapOfReplysToCurrentRequest.containsValue(Boolean.FALSE)){
    		internalClientService.getLog().add(new LogObject("[" + thisNode.getName() +"]Not all responses allowing to write"));
    		for (ClientRmiNode otherNode : internalClientService.getClients().keySet()){
        		internalClientService.getClients().get(otherNode).sendRelease(request);
        	}
    		internalClientService.getQueue().remove(request);
    		return "redirect:/";
    	}else{
    	if (opType.equals(OperationType.WRITE)){
    	// this happens only if i can write    	
	    	externalServerServiceRmi.writeData(thisNode.getName(), shared.getSharedProperty());//zapisem premennu zo servra
	    	internalClientService.setNumberWrap(new NumberWrapper(shared.getSharedProperty())); //premennu zo servra nastavim ako svoju aktualne platnu internu premennu
	    	internalClientService.getLog().add(new LogObject("[" + thisNode.getName() +"]Releasing all clients after successful request for write " + shared.getSharedProperty()));
    	}else{
    		//this happens only if i can read
    		int currentValue = externalServerServiceRmi.readData(thisNode.getName()); //nacitam premennu z klienta
        	internalClientService.setNumberWrap(new NumberWrapper(currentValue));  //premennu zo servra nastavim ako svoju aktualne platnu internu premennu
        	internalClientService.getLog().add(new LogObject("Reading shared property - current val is " + currentValue));
        	internalClientService.getLog().add(new LogObject("[" + thisNode.getName() +"]Releasing all clients after successful request for read " + currentValue));
    	}
    	
    	for (ClientRmiNode otherNode : internalClientService.getClients().keySet()){
    		internalClientService.getClients().get(otherNode).sendRelease(request);
    	}
    	internalClientService.getQueue().remove(request); //odmazat aj zo svojej prioritnej fronty
    	return "redirect:/";
    	}
    }
    
  //stisknute tlacitko login (stisk tlacitka vyvolava POST)
    @RequestMapping(value="/", method=RequestMethod.POST, params="login")
    public String login(Model model){
    	try{
    		externalServerServiceRmi.testConnection(); //otestovat, ci je server zivy
    	}catch(Exception ex){
    		internalClientService.getLog().add(new LogObject("Unable to access server"));
    		internalClientService.setLoggedIn(false);
    		internalClientService.setServerNode(null);
    		return "error";
    	}
    	
    	externalServerServiceRmi.login(thisNode);
    	internalClientService.setLoggedIn(true);
    	internalClientService.getLog().add(new LogObject("Login"));
    	return "redirect:/"; //znova vykresli aktualnu stranku
    }
    
  //stisknute tlacitko logout (stisk tlacitka vyvolava POST)
    @RequestMapping(value="/", method=RequestMethod.POST, params="logout")
    public String logout(Model model){
    	try{
    		externalServerServiceRmi.testConnection(); //otestovat, ci je server zivy
    	}catch(Exception ex){
    		internalClientService.getLog().add(new LogObject("Unable to access server"));
    		internalClientService.setLoggedIn(false);
    		internalClientService.setServerNode(null);
    		return "error";
    	}
    	
    	externalServerServiceRmi.logout(thisNode);
    	internalClientService.setLoggedIn(false);
    	internalClientService.getLog().add(new LogObject("Logout"));
    	return "redirect:/";//znova vykresli aktualnu stranku
    }
    
	public ClientRmiNode getThisNode() {
		return thisNode;
	}

	public void setThisNode(ClientRmiNode thisNode) {
		this.thisNode = thisNode;
	}

	public ExternalServerService getExternalServerServiceRmi() {
		return externalServerServiceRmi;
	}

	public void setExternalServerServiceRmi(
			ExternalServerService externalServerServiceRmi) {
		this.externalServerServiceRmi = externalServerServiceRmi;
	}

	public InternalClientService getClientNodesDao() {
		return internalClientService;
	}

	public void setClientNodesDao(InternalClientService clientNodesDao) {
		this.internalClientService = clientNodesDao;
	}

	public ExternalClientService getExternalClientService() {
		return externalClientService;
	}

	public void setExternalClientService(ExternalClientService externalClientService) {
		this.externalClientService = externalClientService;
	}

	public InternalClientService getInternalClientService() {
		return internalClientService;
	}

	public void setInternalClientService(InternalClientService internalClientService) {
		this.internalClientService = internalClientService;
	}


}
